/**
 * Copyright (C) 2021 - 2031 O-Cubes Co., Ltd.
 */

/****************************************************************
 *  @file    vectors.S
 *  @brief   define default vector handlers. Should use with
 *           GCC for E906 Processors
 *  @version v1.0
 *  @date    03. Apr. 2023
 ****************************************************************/

/* Enable interrupts when return from the handler */
#define MSTATUS_PRV1         0x3880
#define CLICINTIP_BASE       0xE0801000

.section .bss
	.align  2
	.globl  g_trapstackalloc
	.global g_trapstackbase
	.global g_top_trapstack
g_trapstackalloc:
g_trapstackbase:
	.space  768
g_top_trapstack:

	.align  2
	.globl  g_trap_sp
	.type   g_trap_sp, object
g_trap_sp:
	.long   0
	.size   g_trap_sp, .-g_trap_sp

irq_nested_level:
.long 0

.text

	.align  2
	.global Default_IRQHandler
	.weak   Default_IRQHandler
	.type   Default_IRQHandler, %function

/* 外部中断处理 */
Default_IRQHandler:
	/* 分配8字节栈空间 */
	addi    sp, sp, -8
	/* t0 t1入栈 */
	sw      t0, 0(sp)
	sw      t1, 4(sp)

	/* irq_nested_level中断嵌套层数, 初始值为0 */
	/* 若当前中断嵌套层数 > 1, 说明已经在中断上下文, 不需要切换到中断栈, 直接跳转执行Lnested1 */
	la      t0, irq_nested_level
	lw      t1, (t0)
	addi    t1, t1, 1
	sw      t1, (t0)
	li      t0, 1
	bgt     t1, t0, .Lnested1

	/* t0 t1出栈, 恢复异常前的值 */
	lw      t0, 0(sp)
	lw      t1, 4(sp)
	addi    sp, sp, 8

	/**
	 * 所有任务共享中断栈, CPU从正常流程跳转到中断处理的时候, 快速交换sp和中断栈指针,
	 * 保存中断前的栈指针到mscratch
	 */
	csrw    mscratch, sp
	/* 将当前栈指针切换到中断栈 */
	la      sp, g_base_irqstack
	j       .Lnested2

/* 中断多层嵌套, 不需要交换sp和中断栈指针 */
.Lnested1:
	lw      t0, 0(sp)
	lw      t1, 4(sp)
	addi    sp, sp, 8

.Lnested2:
	/* 向中断栈申请72字节空间 */
	addi    sp, sp, -72
	/* t0 t1入中断栈 */
	sw      t0, 4(sp)
	sw      t1, 8(sp)
	/* t0 = mepc, 记录返回地址到t0 */
	csrr    t0, mepc
	/* t1 = mcause, 记录中断类型到t1 */
	csrr    t1, mcause
	/* mepc, mcause入中断栈 */
	sw      t1, 64(sp)
	sw      t0, 68(sp)

	/* x1, (x5, x6即t0, t1已经入栈)x7, x10~x17, x28~x31入栈 */
	sw      ra, 0(sp)
	sw      t2, 12(sp)
	sw      a0, 16(sp)
	sw      a1, 20(sp)
	sw      a2, 24(sp)
	sw      a3, 28(sp)
	sw      a4, 32(sp)
	sw      a5, 36(sp)
	sw      a6, 40(sp)
	sw      a7, 44(sp)
	sw      t3, 48(sp)
	sw      t4, 52(sp)
	sw      t5, 56(sp)
	sw      t6, 60(sp)

	/* t1, mcause */
	andi    t1, t1, 0x3FF
	slli    t1, t1, 2

	/* 在调用do_irq的过程中有可能使用到临时寄存器t1, 会修改t1的内容, 所以这里需要t1入栈 */
	addi    sp, sp, -4
	sw      t1, 0(sp)

	/* 全局中断使能, 此后中断可以再次触发, 中断嵌套执行 */
	csrs    mstatus, 8

	/* 跳转执行do_irq 
	la      t0, do_irq
	jalr    t0  */

	/* 关全局中断 */
	csrc    mstatus, 8

	lw      t1, 0(sp)
	addi    sp, sp, 4

	/* 从中断栈中获取mcause寄存器的值   */
	lw      a1, 64(sp)
	/* 获取中断号 */
	andi    a0, a1, 0x3FF


	/**
	 * CPU响应中断, 需要软件清除中断等待位
	 * 0xE0801000为CLICINTIP基地址
	 */
	li      a2, CLICINTIP_BASE
	add     a2, a2, t1
	lb      a3, 0(a2)
	li      a4, 1
	not     a4, a4
	and     a5, a4, a3
	/* 清除对应中断pending状态 */
	sb      a5, 0(a2)

	/* 获取中断嵌套层数 */
	la      t0, irq_nested_level
	lw      t1, (t0)
	addi    t1, t1, -1
	/* 中断嵌套层数 - 1 */
	sw      t1, (t0)
	/* 中断嵌套     > 0*/
	bgt     t1, zero, .Lnested3

	/**
	 * 硬件响应中断时会将MIE保存到MPIE, 然后设置MIE=0全局禁止中断, 然后设置MPP为0x11为机器模式
	 * mcause.mpil保存了进入中断服务程序前的中断优先级, MINTSTATUS.MPIL代表当前处理的中断的优先级
	 * 即进入中断前MINTSTATUS.MPIL->mcause.mpil
	 * 处理中断时MINTSTATUS.MPIL更新为当前处理中断优先级
	 * 退出中断前mcause.mpil->MINTSTATUS.MPIL
	 * 以上都是硬件自动完成
	 */
	/**
	 * 设置mstatus=MSTATUS_PRV1(0x1880), 设置MPP=0x11, MPIE=0x1, 表明进入中断之前是机器模式,
	 * 处理器进入中断服务程序之前MIE位的值=1
	 */
	li      t0, MSTATUS_PRV1
	csrs    mstatus, t0
	/* 恢复mcause, 因为硬件会将mcause.mpil更新到MINTSTATUS.MPIL */
	csrw    mcause, a1

	/* mepc等寄存器出栈 */
	lw      t0, 68(sp)
	csrw    mepc, t0
	lw      ra, 0(sp)
	lw      t0, 4(sp)
	lw      t1, 8(sp)
	lw      t2, 12(sp)
	lw      a0, 16(sp)
	lw      a1, 20(sp)
	lw      a2, 24(sp)
	lw      a3, 28(sp)
	lw      a4, 32(sp)
	lw      a5, 36(sp)
	lw      a6, 40(sp)
	lw      a7, 44(sp)
	lw      t3, 48(sp)
	lw      t4, 52(sp)
	lw      t5, 56(sp)
	lw      t6, 60(sp)

	/* 恢复到中断前的栈指针 */
	csrr    sp, mscratch
	mret

.Lnested3:
	/* 恢复mstatus和mcause */
	li      t0, MSTATUS_PRV1
	csrs    mstatus, t0
	csrw    mcause, a1
	/* mepc等寄存器出栈 */
	lw      t0, 68(sp)
	csrw    mepc, t0
	lw      ra, 0(sp)
	lw      t0, 4(sp)
	lw      t1, 8(sp)
	lw      t2, 12(sp)
	lw      a0, 16(sp)
	lw      a1, 20(sp)
	lw      a2, 24(sp)
	lw      a3, 28(sp)
	lw      a4, 32(sp)
	lw      a5, 36(sp)
	lw      a6, 40(sp)
	lw      a7, 44(sp)
	lw      t3, 48(sp)
	lw      t4, 52(sp)
	lw      t5, 56(sp)
	lw      t6, 60(sp)

	/* 回收当前中断申请的栈空间，指向上一级中断的栈指针 */
	addi    sp, sp, 72
	/* 中断返回 */
	mret

/******************************************************************************
 * Functions:
 *     void trap(void);
 * default exception handler
 ******************************************************************************/
	.align  2
	.global trap
	.type   trap, %function
trap:
	/* Check for interrupt */
	/* sp = sp-4, 栈向下增长(高地址->低地址) */
	addi    sp, sp, -4
	/* t0入栈 */
	sw      t0, 0x0(sp)
	csrr    t0, mcause
	/* mcause最高位为1,     异常来源为中断 */
	blt     t0, x0, .Lirq

	/* 异常处理流程 */
	addi    sp, sp, 4

	/* t0=g_trap_sp */
	la      t0, g_trap_sp
	/* 向g_trap_sp申请132字节空间, 并将x1~x31, mepc, mstatus入栈 */
	addi    t0, t0, -132
	sw      x1, 0(t0)
	sw      x2, 4(t0)
	sw      x3, 8(t0)
	sw      x4, 12(t0)
	sw      x6, 20(t0)
	sw      x7, 24(t0)
	sw      x8, 28(t0)
	sw      x9, 32(t0)
	sw      x10, 36(t0)
	sw      x11, 40(t0)
	sw      x12, 44(t0)
	sw      x13, 48(t0)
	sw      x14, 52(t0)
	sw      x15, 56(t0)
	sw      x16, 60(t0)
	sw      x17, 64(t0)
	sw      x18, 68(t0)
	sw      x19, 72(t0)
	sw      x20, 76(t0)
	sw      x21, 80(t0)
	sw      x22, 84(t0)
	sw      x23, 88(t0)
	sw      x24, 92(t0)
	sw      x25, 96(t0)
	sw      x26, 100(t0)
	sw      x27, 104(t0)
	sw      x28, 108(t0)
	sw      x29, 112(t0)
	sw      x30, 116(t0)
	sw      x31, 120(t0)
	/* a0=mepc->t0, mepc入栈 */
	csrr    a0, mepc
	sw      a0, 124(t0)
	/* a0=mstatus->t0, mstatus入栈 */
	csrr    a0, mstatus
	sw      a0, 128(t0)

	/* a0=t0(g_trap_sp-132) */
	mv      a0, t0
	/* t0=sp-4, 恢复异常前的t0的值 */
	lw      t0, -4(sp)
	/* sp=a0(t0=g_trap_sp-132), 即将sp切换到新的栈指针(g_trap_sp-132) */
	mv      sp, a0
	/* 将异常发生前的t0的值压入新的栈 */
	sw      t0, 16(sp)

	/* 挑战执行trap_c */
	csrr    a0, mcause
	jal     trap_c
	/* 异常处理通常不需要恢复现场 */


.Lirq:
	/* t0=sp+0, t0出栈 */
	lw      t0, 0x0(sp)
	addi    sp, sp, 4
	/* 跳转执行Default_IRQHandler, 外部中断 */
	j       Default_IRQHandler

	.align  6
	.weak   Default_Handler
	.global Default_Handler
	.type   Default_Handler, %function
/* CLINT处理入口 */
Default_Handler:
	j       trap
	.size   Default_Handler, . - Default_Handler

/*   Macro to define default handlers. Default handler
 *   will be weak symbol and just dead loops. They can be
 *   overwritten by other handlers */
	.macro  def_irq_handler handler_name
	.weak   \handler_name
	.globl  \handler_name
	.set    \handler_name, Default_Handler
	.endm

	def_irq_handler tspend_handler
	def_irq_handler CORET_IRQHandler

